﻿using System.Collections.Generic;
using System;
using System.Reflection;
using MigratorCore.Bases;
using System.IO;
using System.Runtime.Remoting;
using System.Diagnostics;
using MigratorCore.Actions;
using MigratorCore.Objects;
using System.Threading;
using MigratorCore.Status;
namespace MigratorCore
{
    /// <summary>
    /// The Main Migrator
    /// </summary>
    public sealed class Migrator
    {
        private Bases.IProvider source;
        private Bases.IProvider destination;

        /// <summary>
        /// Event fired when the migrator changes
        /// </summary>
        public event Bases.MirgratorChangedEventHandler Change;

        /// <summary>
        /// Holds the Selected Source Provider
        /// </summary>
        public MigratorCore.Bases.IProvider Source
        {
            get { return source; }
            set { source = value; }
        }

        /// <summary>
        /// Holds the Selected Destination Provider
        /// </summary>
        public MigratorCore.Bases.IProvider Destination
        {
            get { return destination; }
            set { destination = value; }
        }

        private List<Bases.IProvider> providers;
        /// <summary>
        /// Holds the List of Providers
        /// </summary>
        public List<Bases.IProvider> Providers
        {
            get
            {
                if (providers == null)
                {
                    Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
                    Debug.AutoFlush = true;
                    Debug.Indent();
                    Debug.WriteLine("DEBUG:Finding Assemblies in Executing Directory");
                    
                    providers = new List<IProvider>();

                    Assembly currentAssembly = Assembly.GetExecutingAssembly();
                    string path = currentAssembly.Location;
                    path = Path.GetDirectoryName(path);
                    foreach (string file in Directory.GetFiles(path, "*.dll"))
                        if (file != currentAssembly.Location)
                            try
                            {
                                Assembly childAssembly = Assembly.LoadFile(file);
                                Debug.WriteLine("DEBUG:Found Assembly " + childAssembly.GetName().Name);
                                Debug.WriteLine("DEBUG:Finding Providers in Assembly " + childAssembly.GetName().Name);
                                foreach (Type t in childAssembly.GetTypes())
                                    foreach (Type ty in t.GetInterfaces())
                                        if (ty == typeof(IProvider))
                                        {
                                            Debug.WriteLine("DEBUG:Found Provider " + t.ToString());
                                            ObjectHandle handle = Activator.CreateInstanceFrom(childAssembly.Location, t.ToString());
                                            IProvider provider = (IProvider)handle.Unwrap();
                                            providers.Add(provider);
                                        }
                            }
                            catch
                            { //Ignore Assembly
                            }
                    Debug.Unindent();
                }
                return providers;
            }
        }

        private List<Thread> threads = new List<Thread>();

        /// <summary>
        /// Fires a new Thread for Each IAction that is needed
        /// </summary>
        public void Start()
        {
            if (sourceActions.Count < 1 || destinationActions.Count < 1)
                throw new Exception("Too little Actions in the Order");
            //Loop through selected actions and start threads for the actions to run in
            threads = new List<Thread>();
            for (int x = 0; x < sourceActions.Count; x++)
            {
                sourceActions[x].Status.Change += new MirgratorChangedEventHandler(OnChanged);
                destinationActions[x].Status.Change += new MirgratorChangedEventHandler(OnChanged);
            }
            for (int x = 0; x < sourceActions.Count; x++)
            {
                ISourceAction sa = sourceActions[x];
                IDestinationAction da = destinationActions[x];
                MigratorRunAction worker = new MigratorRunAction(ref sa, ref da);
                Thread oThread = new Thread(new ThreadStart(worker.Run));
                threads.Add(oThread);
                oThread.Start();
                if (sa.ActionType == ActionType.Users && da.ActionType == ActionType.Users)
                {
                    oThread.Join();  //Causes the for loop to wait until users have finished!
                    foreach (Event e in sa.Status)
                        if (e.EventType == EventType.CriticalError) return;
                    foreach (Event e in da.Status)
                        if (e.EventType == EventType.CriticalError) return;
                }
            }
        }

        /// <summary>
        /// Stops any open threads
        /// </summary>
        public void Stop()
        {
            foreach (Thread othread in threads)
                othread.Abort();
        }

        /// <summary>
        /// Fires Change Event when an Action Status event is fired
        /// </summary>
        private void OnChanged()
        {
            if (Change!= null)
                Change();
        }

        /// <summary>
        /// Default Constructor
        /// </summary>
        public Migrator()
        {
            source = null;
            destination = null;
            providers = null;
            sourceActions = new List<ISourceAction>();
            destinationActions = new List<IDestinationAction>();
        }

        /// <summary>
        /// Private List of Selected Source Actions (Should use ref to add actions)
        /// </summary>
        private List<ISourceAction> sourceActions = new List<ISourceAction>();

        /// <summary>
        /// List of Source Actions (Use ref to Add Actions)
        /// </summary>
        /// <value>Returns or Accepts a List of ISourceAction, or a single instance of an ISourceAction</value>
        public List<ISourceAction> SourceActions
        {
            get { return sourceActions; }
            set { sourceActions = value; }
        }

        /// <summary>
        /// Private List of Selected Destination Actions (Should use ref to add actions)
        /// </summary>
        private List<IDestinationAction> destinationActions = new List<IDestinationAction>();

        /// <summary>
        /// List of Destination Actions (Use ref to Add Actions)
        /// </summary>
        /// <value>Returns or Accepts a List of IDestionationAction, or a single instance of an IDestinationAction</value>
        public List<IDestinationAction> DestinationActions
        {
            get { return destinationActions; }
            set { destinationActions = value; }
        }
    }
}
